﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace TagCore
{
    public class TagCoreUtil
    {
        private static string[] ignoreWordsList = new string[0];

        public static string[] SplitStrings(string val)
        {
            return val.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
        }

        public static string CombineStrings(string[] vals)
        {
            string val = string.Empty;
            if (vals != null)
            {
                foreach (string v in vals)
                {
                    if (val.Length > 0)
                        val += ";";
                    val += v;
                }
            }
            return val;
        }

        public static string StringSearch(string source, string lookup, string finish)
        {
            int start = source.IndexOf(lookup);
            if (start != -1)
            {
                start += lookup.Length;
                int end = source.IndexOf(finish, start);
                if (end != -1)
                {
                    return source.Substring(start, end - start).Trim();
                }
            }
            return string.Empty;
        }

        public static bool PropertiesEqual<T>(T self, T to, params string[] ignore) where T : class
        {
            if (self != null && to != null)
            {
                Type type = typeof(T);
                List<string> ignoreList = new List<string>(ignore);
                foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
                {
                    if (!ignoreList.Contains(pi.Name))
                    {
                        object selfValue = type.GetProperty(pi.Name).GetValue(self, null);
                        object toValue = type.GetProperty(pi.Name).GetValue(to, null);

                        if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)))
                        {
                            return false;
                        }
                    }
                }
                return true;
            }
            return self == to;
        }

        public static string MatchTitle(string[] titles, string p)       
        {
            return doFuzzyMatch(titles, p);
        }

        /// <summary> 
        /// if we need to ignore this target. 
        /// </summary> 
        /// <param name="targetString">The target string.</param> 
        /// <returns></returns> 
        private static bool doIgnore(String targetString) 
        { 
            if ((targetString != null) && (targetString != String.Empty)) 
            { 
                for (int i = 0; i < ignoreWordsList.Length; ++i) 
                { 
                    //* if we found ignore word or target string matching some some special cases like years (Regex). 
                    if (targetString == ignoreWordsList[i]) return true;// || (isMatchInSpecialCases(targetString))) return true; 
                } 
            } 
 
           return false; 
        } 
 
        /// <summary> 
        /// Removes the duplicates. 
        /// </summary> 
        /// <param name="list">The list.</param> 
        private static void removeDuplicates(List<String> list) 
        { 
            if ((list != null) && (list.Count > 0)) 
            { 
                for (int i = 0; i < list.Count - 1; ++i) 
                { 
                    if (list[i] == list[i + 1]) 
                    { 
                        list.RemoveAt(i); 
                        --i; 
                    } 
                } 
            } 
        } 
 
        /// <summary> 
        /// Does the fuzzy match. 
        /// </summary> 
        /// <param name="targetTitle">The target title.</param> 
        /// <returns></returns> 
        private static string doFuzzyMatch(string[] titles, string targetTitle)
        {
            string matchResult = string.Empty;
            float minimalScore = 0.5f;

            if (targetTitle != null && targetTitle != String.Empty)
            {
                try
                {
                    //* change target title (string) to lower case. 
                    targetTitle = targetTitle.ToLower();

                    //* scores, we will select higher score at the end. 
                    Dictionary<string, float> scores = new Dictionary<string, float>();

                    char[] ignoreCharsList = new char[] { '-', ' ', '.', ',', '?', '/', ':', ';', '%', '(', ')', '#', '\"', '\'', '!', '|', '^', '*', '[', ']', '{', '}', '=', '!', '+', '_' }; 
                    List<String> targetKeywords = new List<string>(targetTitle.Split(ignoreCharsList, StringSplitOptions.RemoveEmptyEntries));

                    //* remove all trash from keywords, like super, quality, etc.. 
                    targetKeywords.RemoveAll(delegate(String x) { return doIgnore(x); });
                    //* sort keywords. 
                    targetKeywords.Sort();
                    //* remove some duplicates. 
                    removeDuplicates(targetKeywords);

                    //* go through all original titles. 
                    foreach (string sourceTitle in titles)
                    {
                        float tempScore = 0f;
                        //* split orig. title to keywords list. 
                        List<String> sourceKeywords = new List<string>(sourceTitle.Split(ignoreCharsList, StringSplitOptions.RemoveEmptyEntries));
                        sourceKeywords.Sort();
                        removeDuplicates(sourceKeywords);

                        //* go through all source ttl keywords. 
                        foreach (String keyw1 in sourceKeywords)
                        {
                            float max = float.MinValue;
                            foreach (String keyw2 in targetKeywords)
                            {
                                float currentScore = CalculateSimilarity(keyw1.ToLower(), keyw2);
                                if (currentScore > max)
                                {
                                    max = currentScore;
                                }
                            }
                            tempScore += max;
                        }

                        //* calculate average score. 
                        float averageScore = (tempScore / Math.Max(targetKeywords.Count, sourceKeywords.Count));

                        //* if average score is bigger than minimal score and target title is not in this source title ignore list. 
                        if (averageScore >= minimalScore)
                        {
                            //* add score. 
                            scores.Add(sourceTitle, averageScore);
                        }
                    }

                    //* choose biggest score. 
                    float maxi = float.MinValue;
                    foreach (KeyValuePair<string, float> kvp in scores)
                    {
                        if (kvp.Value > maxi)
                        {
                            maxi = kvp.Value;
                            matchResult = kvp.Key;
                        }
                    }
                }
                catch { }
            }
            //* return result. 
            return matchResult;
        }

        /// <summary> 
        /// Gets the similarity between two strings. 
        /// All relation scores are in the [0, 1] range,  
        /// which means that if the score gets a maximum value (equal to 1)  
        /// then the two string are absolutely similar 
        /// </summary> 
        /// <param name="string1">The string1.</param> 
        /// <param name="string2">The string2.</param> 
        /// <returns></returns> 
        private static float CalculateSimilarity(String s1, String s2)
        {
            if ((s1 == null) || (s2 == null)) return 0.0f;

            float dis = (float)Compute(s1, s2);
            float maxLen = s1.Length;
            if (maxLen < s2.Length)
                maxLen = s2.Length;
            if (maxLen == 0.0F)
                return 1.0F;
            else return 1.0F - dis / maxLen;
        }

        private static int Compute(string s, string t)
        {
            int n = s.Length;
            int m = t.Length;
            int[,] d = new int[n + 1, m + 1];

            // Step 1
            if (n == 0)
            {
                return m;
            }

            if (m == 0)
            {
                return n;
            }

            // Step 2
            for (int i = 0; i <= n; d[i, 0] = i++)
            {
            }

            for (int j = 0; j <= m; d[0, j] = j++)
            {
            }

            // Step 3
            for (int i = 1; i <= n; i++)
            {
                //Step 4
                for (int j = 1; j <= m; j++)
                {
                    // Step 5
                    int cost = (t[j - 1] == s[i - 1]) ? 0 : 1;

                    // Step 6
                    d[i, j] = Math.Min(
                        Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
                        d[i - 1, j - 1] + cost);
                }
            }
            // Step 7
            return d[n, m];
        }
    }
}
